/*
 *   This file is part of ed_pvr.
 *
 *   ed_pvr is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   ed_pvr is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with ed_pvr.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <inttypes.h>
#include <stdint.h>
#include <memory.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <time.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <poll.h>
#include <math.h>
#include <zlib.h>
#include "utils.h"

#define RW_TIMEOUT 100
#define ZLIB_CHUNK 16384
static const uint32_t __crc_table[256] =
{
        0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
        0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
        0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
        0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
        0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
        0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
        0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
        0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
        0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
        0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
        0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
        0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
        0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
        0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
        0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
        0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
        0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
        0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
        0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
        0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
        0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
        0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
        0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
        0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
        0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
        0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
        0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
        0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
        0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
        0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
        0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
        0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
        0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
        0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
        0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
        0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
        0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
        0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
        0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
        0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
        0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
        0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
        0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};



uint32_t utils_crc32(uint8_t *data, uint32_t len)
{
	uint32_t i;
	uint32_t crc = 0xFFFFFFFF;

	for (i = 0; i < len; i++)
		crc = (crc << 8) ^ __crc_table[((crc >> 24) ^ *data++) & 0xFF];
	return crc;
}




unsigned int str_hash(char *str)
{
   unsigned int h;
   unsigned char *p;

   h = 0;
   for (p = (unsigned char*)str; *p != '\0'; p++)
      h = 37 * h + *p;
   return h; // or, h % ARRAY_SIZE;
}

// ------------------------------
TEXTFILE_IN_MEMORY * memfile_init(char *data, int len)
{
	TEXTFILE_IN_MEMORY * ret = NULL;
	if (data && len)
	{
		ret = calloc(1, sizeof(TEXTFILE_IN_MEMORY));
		ret->data = (uint8_t *)data;
		ret->data_size = len;
		if (1)
		{
			int i;
			for(i=0; i < len; i++)
			{
				if(ret->data[i] == '\n')
					ret->line_count++;
			}
		}
	}
	return ret;
}

int memfile_getline_count(TEXTFILE_IN_MEMORY *m)
{
	if (m)
		return m->line_count;
	return -1;
}

char * memfile_getnext_line(TEXTFILE_IN_MEMORY *m, int mem)
{
	char *ret = NULL;
	if (m && m->pos < m->data_size)
	{
		int i;

		for(i=m->pos; i < m->data_size; i++)
		{
			if (m->data[i] == '\n' || m->data[i] == '\0')
			{
				m->data[i] = '\0';
				break;
			}
		}

		if (i > m->pos)
		{
			ret = (char *) &m->data[m->pos];
			m->pos = (i+1);
		}
	}
	return ret;
}

int get_bytes_from_file(int handle, uint8_t * data, int len)
{
	return read(handle, data, len);

}
/*
// Lifted from the XBMC code
ssize_t get_bytes_from_socket2(int socket, uint8_t * data, int len, uint64_t iTimeoutMs, int *iError)
{
	int tmp;
    int64_t iNow = 0, iTarget =0;
    ssize_t iBytesRead  = 0;
	if (iError == NULL)
		iError = &tmp;

    *iError = 0;

    if (socket == -1)
    {
      *iError = EINVAL;
//		printf("Bad1\n");
      return -EINVAL;
    }

    if (iTimeoutMs > 0)
    {
      iNow    = get_time_ms();
      iTarget = iNow + (int64_t) iTimeoutMs;
//	 printf("iNow: %" PRId64", iTarget: %" PRId64" -- iTimeout: %" PRId64"\n", iNow, iTarget, iTimeoutMs);
    }

    struct pollfd fds;
    fds.fd = socket;
    fds.events = POLLIN;
    fds.revents = 0;

    while (iBytesRead >= 0 && iBytesRead < (ssize_t)len && (iTimeoutMs == 0 || iTarget > iNow))
    {
      if (iTimeoutMs > 0)
      {
        int iPollResult = poll(&fds, 1, iTarget - iNow);
//        int iPollResult = poll(&fds, 1, 1);
        if (iPollResult == 0)
        {
//		printf("ETIMEDOUT 0: %d\n", iPollResult);
          *iError = ETIMEDOUT;
          return -ETIMEDOUT;
        }
      }

      ssize_t iReadResult = (iTimeoutMs > 0) ?
          recv(socket, (char*)data + iBytesRead, len - iBytesRead, MSG_DONTWAIT) :
          recv(socket, data, len, MSG_WAITALL);
      if (iReadResult < 0)
      {
        if (errno == EAGAIN && iTimeoutMs > 0)
          continue;
        *iError = errno;

        return -errno;
      }
      else if (iReadResult == 0 || (iReadResult != (ssize_t)len && iTimeoutMs == 0))
      {
        *iError = ECONNRESET;
        return -ECONNRESET;
      }

      iBytesRead += iReadResult;

      if (iTimeoutMs > 0)
        iNow = get_time_ms();
    }

    if (iBytesRead < (ssize_t)len)
	{
		*iError = ETIMEDOUT;
//		printf("ETIMEDOUT 2: %"PRId64" %d\n", iBytesRead, len);
	}
    return iBytesRead;
}
*/
ssize_t get_bytes_from_socket(int handle, uint8_t * data, int len, uint64_t iTimeoutMs, int *iError)
{
//	uint8_t nonblock = 0;
//	int tmp_count = 0;

	if (handle > 2 && data && len > 0)
	{
		int bytes_read = 0, rc = 0;
		int want = len;

		if (iTimeoutMs)
		{
			long int us = iTimeoutMs*1000;
			long int s = iTimeoutMs/1000;

			uint64_t now = get_time_ms();
			uint64_t end = now + iTimeoutMs;
//			printf("get_bytes_from_socket 1\n");
			while(bytes_read < len)
			{
//			printf("get_bytes_from_socket 2\n");
				rc = select_wait_read(handle, s, us);
//			printf("get_bytes_from_socket 3: %d\n", rc);

				if (rc <= -1)
				{
					printf("[UTILS] TimeOut on select(): %s (%d) (h: %d)\n", strerror(errno), errno, handle);
					return -2;
				}
				else if (rc == 0)
				{
					now = get_time_ms();
					if (now > end)
					{
						//printf("TIMEOUT!\n");
						return bytes_read;
					}
				}
//			printf("get_bytes_from_socket 4: %d\n", rc);

				rc = recv(handle, &data[bytes_read], want, MSG_DONTWAIT);
//			printf("get_bytes_from_socket 4a: %d\n", rc);

				if (rc == -1)
				{
					if (errno == EWOULDBLOCK)
					{
//						printf("blocking: %d\n", bytes_read);
						now = get_time_ms();
						if (now > end)
						{
							//printf("TIMEOUT!\n");
							return bytes_read;
						}

						continue;
					}
//					printf("recv error: %d [%s - %d]\n", rc,  strerror(errno), errno);
					return -2;
				}
				else if (rc > 0)
				{
					want -= rc;
					bytes_read += rc;
				}
				else if (rc == 0)
				{
					now = get_time_ms();
					if (now > end)
					{
						//printf("TIMEOUT!\n");
						return bytes_read;
					}
				}
//			printf("get_bytes_from_socket 5\n");
			}
		}
		else
			bytes_read = recv(handle, data, want, MSG_WAITALL);

		if (bytes_read > 0)
		{
//			printf("Read: %d\n", bytes_read);
			return bytes_read;
		}
		return -1;
	}
	return -1;
}

ssize_t get_string_from_socket(int handle, uint8_t * data, int len)
{
	ssize_t ret = -1;

	if (handle > 2 && data && len > 0)
	{
		int64_t start = 0, now = 0;
		ssize_t tmp = 0;
		int pos = 0, err = 0;
		uint8_t last_count = 0, cur = 0;

		start = time(NULL);
		while (pos < len)
		{
			tmp = get_bytes_from_socket(handle, &cur, 1, 1000, &err);

			if (tmp > 0)
			{
				data[pos++] = cur;
				switch(cur)
				{
					case 0x0A:
						last_count++;
					break;

					case 0x0D:
					break;

					default:
						last_count=0;
					break;
				}

				if (last_count >= 2)
					break;
			}
			else if (tmp < 0)
			{
				printf("get_string_from_socket, bad return\n");
				ret = tmp;
				break;
			}
			else if (tmp == 0)
			{
				if (pos > 2)
				{
//					printf("get_string_from_socket, gonna exit early: %d (%s) [0x%x 0x%x 0x%x 0x%x]\n", pos, (char*)data, data[pos-3], data[pos-2], data[pos-1], data[pos]);
					break;
				}
//				printf("get_string_from_socket, sleep to try again %d\n", pos);
//				usleep(1000*200);
			}

			now = time(NULL);
			if (now - start > 5)
			{
				printf("get_string_from_socket, no more sleep: %d (start: %li, now: %li)\n", pos, now, start);
				break;
			}

		}
		ret = (ssize_t)pos;
	}
	return ret;
}

ssize_t write_bytes_to_socket2(int handle, uint8_t * data, int len, uint64_t iTimeoutMS, int *error)
{
	int bytes_out = -1;
	int tmp_count = 0;
	long int us = iTimeoutMS*1000;
	long int s = iTimeoutMS/1000;

	if (handle >= 0 && data && len > 0)
	{
		int bytes_write = 0, rc = 0;
		int want = len;

		rc = select_wait_write(handle, s, us);
		if (rc == -1)
		{
			printf("write_bytes_to_socket blag: %d %ld %ld\n", handle, s, us);
			return rc;
		}
		bytes_out = 0;
		tmp_count = 0;

		while (bytes_out < len)
		{
			bytes_write = write(handle, &data[bytes_out], want);

 			if (bytes_write == -1 || tmp_count > RW_TIMEOUT)
			{
				if (bytes_out <= 0)
					return -1;
				else
					return bytes_out;
			}
			else if (bytes_write == 0)
			{
				usleep(1000);
			}
			tmp_count++;

			bytes_out += bytes_write;
			want -= bytes_write;
		}
	}
	return bytes_out;
}


ssize_t write_bytes_to_socket(int socket, uint8_t * data, int len, uint64_t iTimeoutMS, int *iError)
{
	if (socket >= 0 && data && len > 0)
	{
		int rc = 0;
		long int us = iTimeoutMS*1000;
		long int s = iTimeoutMS/1000;
		int64_t start = time(NULL);

		while(rc == 0)
		{
//			rc = select_wait_write(socket, 2, 0);
			rc = select_wait_write(socket, s, us);
			if (rc == -1)
			{
				printf("write_bytes_to_socket err\n");
				return -1;
			}
			else if (rc == 0)
			{
				int64_t now = time(NULL);
				if (now - start > iTimeoutMS/1000)
				{
					printf("write_bytes_to_socket timeout\n");
					return -2;
				}
			}
		}
		return (ssize_t)send(socket, data, len, MSG_WAITALL);
//		return (ssize_t)send(socket, data, len, MSG_WAITALL);
	}
	return -1;
}



off_t get_file_size(char *filename)
{
    if (filename)
    {
        struct stat st1 = {0};
        stat(filename, &st1);
        return st1.st_size;
    }
    return 0;
}

off_t get_file_size_handle(int fh)
{
    if (fh)
    {
        struct stat st1 = {0};
        fstat(fh, &st1);
        return st1.st_size;
    }
    return 0;
}

int8_t file_exists(char *filename)
{
	int fh = open(filename, O_RDONLY);
	if (fh <= 0)
		return -1;
	close(fh);
	return 0;
}

unsigned int htoi (const char *ptr)
{
	unsigned int value = 0;
	char ch = *ptr;

	while (ch == ' ' || ch == '\t')
		ch = *(++ptr);

	for (;;)
	{
		if (ch >= '0' && ch <= '9')
			value = (value << 4) + (ch - '0');
		else if (ch >= 'A' && ch <= 'F')
			value = (value << 4) + (ch - 'A' + 10);
		else if (ch >= 'a' && ch <= 'f')
			value = (value << 4) + (ch - 'a' + 10);
		else
			return value;
		ch = *(++ptr);
	}
}


char *url_encode(char *input)
{
	char c;
	int i, k=0;
	const char *hex = "0123456789ABCDEF";
	char *ret = calloc(1, strlen(input) *2);

	for(i=0; i < strlen(input); i++)
	{
		c = input[i];

		if (c == '%')
		{
			ret[k++] = input[i];
			ret[k++] = input[i+1];
			ret[k++] = input[i+2];
			i += 2;
			continue;
		}

		if( ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') )
		{
			ret[k++] = c;
		}
		else
		{
			ret[k++] = '%';
			ret[k++] = hex[c >> 4];
			ret[k++] = hex[c & 15];
		}
	}
	return ret;
}

char *get_local_time(void)
{
	char *ret = calloc(1, 128);
	int64_t curtime = {0};
	struct tm *loctime = NULL;
	curtime = time (NULL);
	loctime = localtime ((time_t *)&curtime);
	strftime(ret, 128, "%I:%M%P", loctime);
	return ret;
}

char *get_local_date(void)
{
	char *ret = calloc(1, 128);
	int64_t curtime = {0};
	struct tm *loctime = NULL;
	curtime = time (NULL);
	loctime = localtime ((time_t*)&curtime);
	strftime(ret, 128, "%x", loctime);
	return ret;
}



int poll_wait_read(int sock, int timeout_s, int timeout_u)
{
	struct pollfd fds[1];
	int timeout = (timeout_s * 1000) + (timeout_u/1000);
	memset(fds, 0, sizeof(fds));
	fds[0].fd = sock;
	fds[0].events = POLLIN;
	return poll(fds, 1, timeout);
}

int poll_wait_write(int sock, int timeout_s, int timeout_u)
{

	struct pollfd fds[1];
	int timeout = (timeout_s * 1000) + (timeout_u/1000);
	memset(fds, 0, sizeof(fds));
	fds[0].fd = sock;
	fds[0].events = POLLOUT;
	return poll(fds, 1, timeout);
}


int select_wait_read(int sock, int timeout_s, int timeout_u)
{
	fd_set readfds;
	struct timeval tv = {0};

	tv.tv_sec = timeout_s;
	tv.tv_usec = timeout_u;

	FD_ZERO(&readfds);
	FD_SET(sock, &readfds);
	return select(sock+1, &readfds, NULL, NULL, &tv);
}

int select_wait_write(int sock, int timeout_s, int timeout_u)
{
	fd_set fds;
	struct timeval tv = {0};

	tv.tv_sec = timeout_s;
	tv.tv_usec = timeout_u;

	FD_ZERO(&fds);
	FD_SET(sock, &fds);

	return select(sock+1, NULL, &fds, NULL, &tv);
}


int get_listen_socket(char * ip, uint16_t port)
{
	int server_socket = -1;
	socklen_t address_len;
	struct sockaddr_storage address;
	struct addrinfo *addrinfo, hints;
	char portnumber[10];
	int yes = 1;

	sprintf(portnumber, "%d", port);

	memset((void *)&hints, 0, sizeof(hints));
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE;

	if ((getaddrinfo(ip, portnumber, &hints, &addrinfo) != 0) || (addrinfo == NULL))
	{
		printf("[TUNER] getaddrinfo() failed..\n");
		return -1;
	}

	if (addrinfo->ai_addrlen > sizeof(struct sockaddr_storage))
	{
		printf("[TUNER] Weird, failing..\n");
		freeaddrinfo(addrinfo);
		return -1;
	}

	address_len = addrinfo->ai_addrlen;
	memcpy(&address, addrinfo->ai_addr, addrinfo->ai_addrlen);
	freeaddrinfo(addrinfo);

	server_socket = socket(address.ss_family, SOCK_STREAM, IPPROTO_TCP);
	if (server_socket < 0)
	{
		printf("[TUNER] socket() failed\n");
		return -1;
	}

	if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
	{
		printf("[TUNER] setsockopt failed\n");
		return -1;
	}

	if (bind(server_socket, (struct sockaddr *) &address, address_len) < 0)
	{
		printf("[TUNER] Couldn't bind: %s\n", strerror(errno));
		close(server_socket);
		return -1;
	}
	listen(server_socket, 1);
//	printf("[TUNER] Listen on IP/Port: %s/%s\n", ip, portnumber);
	return server_socket;
}


//UTILS_DATAPAIR_STRUCT * utils_zlib_decompress(uint8_t *data_in, uint32_t data_size)
UTILS_DATAPAIR_STRUCT * utils_zlib_decompress(UTILS_DATAPAIR_STRUCT *data_input)
{
	UTILS_DATAPAIR_STRUCT * ret = NULL;

	if (data_input->data && data_input->data_size)
	{
		int err;
		z_stream strm = {0};

		strm.zalloc = Z_NULL;
		strm.zfree = Z_NULL;
		strm.opaque = Z_NULL;
		strm.avail_in = 0;
		strm.next_in = Z_NULL;
		err = inflateInit2(&strm, 15 + 16);

		if (err == Z_OK)
		{
			uint32_t pos = 0, pos2 = 0, r_size = 0, w_size = 0;
			unsigned char in[ZLIB_CHUNK] = {0};
			unsigned char out[ZLIB_CHUNK] = {0};

			ret = calloc(1, sizeof(*ret));

			if (!ret)
				return NULL;

			do
			{
				memset(in, 0, ZLIB_CHUNK);
				memset(out, 0, ZLIB_CHUNK);
				r_size = ZLIB_CHUNK;

				if (data_input->data_size - pos < ZLIB_CHUNK)
					r_size = (data_input->data_size - pos);

				memcpy(in, data_input->data + pos, r_size);
				strm.avail_in = r_size;
				pos += r_size;

				if (strm.avail_in == 0)
					break;

				strm.next_in = in;

				do
				{
					strm.avail_out = ZLIB_CHUNK;
					strm.next_out = out;
					err = inflate(&strm, Z_NO_FLUSH);
					switch (err) {
						case Z_NEED_DICT:
							err = Z_DATA_ERROR;
						case Z_DATA_ERROR:
						case Z_MEM_ERROR:
							(void)inflateEnd(&strm);
						return NULL;
					}
					w_size = ZLIB_CHUNK - strm.avail_out;
					ret->data_size += w_size;
					ret->data = realloc(ret->data, ret->data_size);
					memcpy(ret->data + pos2, out, w_size);
					pos2 += w_size;
				} while (strm.avail_out == 0);

			} while (err != Z_STREAM_END);
			(void)inflateEnd(&strm);
		}
	}
	return ret;
}


size_t generic_curl_handler(void *buffer, size_t size, size_t nmemb, void *userp)
{
	UTILS_DATAPAIR_STRUCT *carg = NULL;

	if (buffer && size > 0 && nmemb > 0 && userp)
	{
		size_t last_sz = 0;
		carg = (UTILS_DATAPAIR_STRUCT *)userp;
		if (!carg->throw_away)
		{
			last_sz = carg->data_size;

			if (carg->data)
			{
				carg->data = realloc(carg->data, carg->data_size + nmemb);
				carg->data_size += nmemb;
			}
			else
			{
				carg->data = calloc(1, nmemb);
				carg->data_size = nmemb;
			}
			memcpy(carg->data + last_sz, buffer, nmemb);
		}
	}
	return nmemb;
}

uint64_t get_time_ms(void)
{
	struct timespec spec;
	clock_gettime(CLOCK_MONOTONIC, &spec);
	return (uint64_t)floor((spec.tv_sec * 1000) + (spec.tv_nsec / 1.0e6));
}

size_t get_bufsize(int handle)
{
	size_t bsize = 0;
	struct stat sbuf = {0};
	if (fstat(handle, &sbuf) == 0)
		bsize = MAX(sbuf.st_blksize, BUFSIZ);

	return bsize;
}

uint32_t get_nearest_page_size(uint32_t sz, uint32_t packet_size)
{
	uint32_t ret = sz;
	int pgsize = getpagesize();

	if (pgsize)
	{
		pgsize *= packet_size;
		ret = (sz/pgsize) * pgsize;
	}
	return ret;
}

int doconnect(char *ip, uint16_t port, int timeout)
{
//	int rc = -1;

	if (ip && port)
	{
		int flags = -1;
		struct addrinfo hints = {0}, *res = NULL;
		int sock = 0, sz;
		char service[13] = {0};
		snprintf(service, 12, "%d", port);

		hints.ai_socktype = SOCK_STREAM;
		hints.ai_family = AF_UNSPEC;
		hints.ai_protocol = IPPROTO_TCP;

		sz = getaddrinfo(ip, service, &hints, &res);
		if (sz != 0)
		{
			printf("getaddrinfo failed\n");
			return -1;
		}

		sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
		if (sock == -1)
		{
			printf("BLAAAAAH\n");
			return -1;
		}

		if (timeout > 0)
		{
			flags = fcntl(sock, F_GETFL, 0);
			fcntl(sock, F_SETFL, flags | O_NONBLOCK);
		}

		sz = connect(sock,res->ai_addr, res->ai_addrlen);

		if (timeout > 0)
		{
			if (select_wait_write(sock, timeout, 0) == 1)
			{
				int so_error;
				socklen_t len = sizeof so_error;
				getsockopt(sock, SOL_SOCKET, SO_ERROR, &so_error, &len);
//				printf("HERE!! %d\n", so_error);
				if (so_error == 0)
				{
					fcntl(sock, F_SETFL, flags);
					return sock;
				}
			}

			close(sock);
			sz = -1;
		}

		if (sz == 0)
		{
			return sock;
		}
		else
		{
			printf("[UTILS] Problem connecting: %s -- %d\n", strerror(errno), sz);
		}

	}
	return -1;
}

UTILS_HOSTPORT_STRUCT * utils_split_host(char *in)
{
	UTILS_HOSTPORT_STRUCT * ret = NULL;
	if (in)
	{
		ret = calloc(1, sizeof(UTILS_HOSTPORT_STRUCT));
		if (ret)
		{
			char *tmpp = NULL;
			char *host = strdup(in);
			int j, sz = strlen(host);

			for (j=0; j < sz; j++)
			{
				if (host[j] == ':')
				{
					host[j] = '\0';
					tmpp = &host[j+1];
				}
				else if (host[j] == '/')
				{
					host[j] = '\0';
				}
			}
			strncpy(ret->hostname, host, 4095);
			ret->port = atoi(tmpp);
			free(host);
		}
	}
	return ret;
}


int utils_free_datapair(UTILS_DATAPAIR_STRUCT *carg)
{
	if (carg)
	{
		if (carg->data)
			free(carg->data);
		free(carg);
		return 0;
	}
	return -1;
}

UTILS_LOG_STRUCT *utils_log_init(void)
{
	UTILS_LOG_STRUCT *ret = calloc(1, sizeof(*ret));
	if (ret)
	{
//		ret->log = utils_ll_init();
//		ret->queue = g_async_queue_new();
		pthread_mutex_init(&ret->log_lock, NULL);
	}
	return ret;
}


char * utils_log_getmsg(UTILS_LOG_STRUCT *log)
{
	char * ret = NULL;
	pthread_mutex_lock(&log->log_lock);
	if (log)
	{
/*
//		UTILS_DATAPAIR_STRUCT *data_pair  = utils_ll_get_first(log->log);
		if (data_pair)
		{
			ret = (char *)data_pair->data;
			utils_ll_del(log->log, data_pair);
		}
*/
	}
	pthread_mutex_unlock(&log->log_lock);
	return ret;
}

void utils_log_print(UTILS_LOG_STRUCT *log, const char *fmt, ...)
{
//	pthread_mutex_lock(&log->log_lock);
	if (log && fmt)
	{
		va_list ap;
		va_start(ap, fmt);
		utils_log_vprint(log, fmt, ap);
		va_end(ap);
	}
//	pthread_mutex_unlock(&log->log_lock);

}

void utils_log_vprint(UTILS_LOG_STRUCT *log, const char *fmt, va_list ap)
{
	pthread_mutex_lock(&log->log_lock);
	if (log && fmt)
	{
		char *msg = calloc(1, UTILS_MAX_LOG_LEN);
		vsnprintf(msg, UTILS_MAX_LOG_LEN-1, fmt, ap);
//		utils_ll_add(log->log, msg, UTILS_MAX_LOG_LEN);
		printf("%s", msg);
	}
	pthread_mutex_unlock(&log->log_lock);

}


int utils_mutex_init(pthread_mutex_t *m, pthread_mutexattr_t *attr, int type)
{
	if (m)
	{
		if (attr)
		{
			pthread_mutexattr_init(attr);
			pthread_mutexattr_settype(attr, type);
		}
		return pthread_mutex_init(m, attr);
	}
	return -1;
}


char * utils_epoch_to_str(int64_t epoch)
{
	char * ret = NULL;
	if(epoch)
	{
		struct tm ts = {0};
		ts = *localtime((time_t *)&epoch);
		ret = malloc(4096);
		strftime (ret, 4096, "%Y%m%d%H%M%S %z", &ts);
	}
	return ret;
}

char * utils_epoch_to_str2(int64_t epoch, uint8_t mode)
{
	char * ret = NULL;
	if(epoch)
	{
		struct tm ts = {0};
		if (mode == 0)
			ts = *gmtime((time_t *)&epoch);
		else
			ts = *localtime((time_t *)&epoch);

		ret = malloc(4096);
		strftime (ret, 4096, "%FT%T%z", &ts);
	}
	return ret;
}


uint64_t utils_str_to_epoch(char *timestr)
{
	if (timestr)
	{
		struct tm tm = {0};
		strptime(timestr, "%Y%m%d%H%M%S", &tm);
		tm.tm_isdst = -1;
		return (uint64_t)mktime(&tm);
	}
	return 0;
}

uint64_t utils_date_to_epoch(char *timestr)
{
	if (timestr)
	{
		struct tm tm = {0};
		strptime(timestr, "%Y%m%d", &tm);
		tm.tm_isdst = -1;
		return (uint64_t)mktime(&tm);
	}
	return 0;
}

uint64_t utils_date_to_epoch2(char *timestr)
{
	if (timestr)
	{
		struct tm tm = {0};
		printf("timestr: %s\n", timestr);
		strptime(timestr, "%I:%M %p", &tm);
		tm.tm_isdst = 0;
		return (uint64_t)timegm(&tm);
	}
	return 0;
}




uint64_t utils_str_to_epoch2(char *str, uint8_t mode)
{
	uint64_t ret = 0;
	int64_t offset = 0;
	if (str)
	{
		struct tm tm = {0};
		strptime(str, "%FT%T%z", &tm);
		tm.tm_isdst = -1;

		if (tm.tm_gmtoff)
		{
			struct tm tm2 = {0};
			time_t _now = time(NULL);
			localtime_r (&_now, &tm2);
//			printf("utils_str_to_epoch2 (%s): %" PRId64" -- %" PRId64"\n", str, tm.tm_gmtoff, tm2.tm_gmtoff);
			if (tm2.tm_gmtoff <  tm.tm_gmtoff)
			{
//				printf("utils_str_to_epoch2, tz is behind: %" PRId64"\n", (tm2.tm_gmtoff - tm.tm_gmtoff)/3600);
                offset = (int64_t)tm2.tm_gmtoff - (int64_t)tm.tm_gmtoff;
			}
			else if (tm2.tm_gmtoff > tm.tm_gmtoff)
			{
//				printf("utils_str_to_epoch2, tz is ahead: %" PRId64"\n", (tm.tm_gmtoff - tm2.tm_gmtoff)/3600);
                offset = (int64_t)tm.tm_gmtoff - (int64_t)tm2.tm_gmtoff;
			}
			if (offset)
			{
//				printf("GMT Offset: %" PRIi64" (%" PRIi64")\n", offset, offset/3600);
			}
		}

		if (mode)
			ret = (uint64_t) timegm(&tm);
		else
			ret = (uint64_t) mktime(&tm);
	}

	return ret + offset;
}


char *utils_split_value_after(char *str, char c)
{
	char *ret = NULL;
	if (str)
	{
//		int i, j;
		int i;
		int len = strlen(str);

		for(i=0; i < len; i++)
		{
			if (str[i] == c)
				break;
		}
		if (i < len)
		{
			ret = malloc(len - i + 1);
			memcpy(ret, &str[i+1], len-i);
		}
	}
	return ret;
}


int b64decode(char *s)
{
	char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	int bit_offset=0, byte_offset=0, idx=0, i=0, n=0;
	unsigned char *d = (unsigned char *)s;
	char *p;
	while (*s && (p=strchr(b64,*s)))
	{
		idx = (int)(p - b64);
		byte_offset = (i*6)/8;
		bit_offset = (i*6)%8;
		d[byte_offset] &= ~((1<<(8-bit_offset))-1);
		if (bit_offset < 3) {
			d[byte_offset] |= (idx << (2-bit_offset));
			n = byte_offset+1;
		} else {
			d[byte_offset] |= (idx >> (bit_offset-2));
			d[byte_offset+1] = 0;
			d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF;
			n = byte_offset+2;
		}
		s++; i++;
	}
	/* null terminate */
	d[n] = 0;
	return n;
}

int str_to_bytes(char *in, uint8_t *out, int size)
{
	int j = 0;
	if (in && out && size)
	{
		int i;
		int len = strlen(in);
		uint8_t _tmp[3];

		if (len / 2 > len)
		{
			printf("set_hex: Error! out buffer too small: %d (%d)\n", size, len);
			return 0;
		}

		if (len %2)
		{
			printf("set_hex: Error! Uneven hex string\n");
			return 0;
		}

		for(i=0; i < len; i+=2)
		{
			_tmp[0] = (uint8_t)in[i];
			_tmp[1] = (uint8_t)in[i+1];
			_tmp[2] = '\0';

			out[j++] = (uint8_t)htoi((char*)_tmp);
		}
	}
	return j;
}


#ifdef _MAIN_

int main(int argc, char *argv[])
{
//	char * r = url_encode("o+e4w3P/tKlYrjjGM3B7PcxsG3A=");
	char * r = url_encode("o%2Be4w3P%2FtKlYrjjGM3B7PcxsG3A%3D3D");
	printf("r: %s\n", r);
	printf("r1: %s\n", "o%2Be4w3P%2FtKlYrjjGM3B7PcxsG3A%3D3D");
	return 0;
}
#endif
